from typing import Optional

from fastapi import FastAPI, Depends, Query, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from sqlalchemy import func
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.future import select
from sqlalchemy.orm import joinedload

from database import get_db, init_db, close_db
from models import User, Order
from schemas import UserResponse, PaginationParams, PaginationResponse, UserCreate, UserUpdate, UpdateUserResponse

# 创建FastAPI应用
app = FastAPI(
    title="FastAPI分页查询示例",
    description="使用FastAPI和MySQL实现的异步分页查询示例",
    version="1.0.0"
)

# 添加CORS中间件
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 使用lifespan上下文管理器替代on_event装饰器
from contextlib import asynccontextmanager


@asynccontextmanager
async def lifespan(app: FastAPI):
    # 启动时执行
    await init_db()
    print("数据库已初始化")
    yield
    # 关闭时执行
    await close_db()
    print("数据库连接已关闭")


# 将lifespan上下文管理器应用到app
app.router.lifespan_context = lifespan


# 根路由
@app.get("/")
async def root():
    return {"message": "FastAPI分页查询示例API"}


'''
关于依赖注入:
在FastAPI中使用`db: AsyncSession = Depends(get_db)` 依赖注入而不是直接在函数中调用`get_db()` 有几个重要原因：

1. 依赖注入模式 ：这是一种设计模式，使代码更易于测试和维护。依赖注入允许FastAPI自动处理依赖项的生命周期。
2. 自动资源管理 ：使用依赖注入，FastAPI会自动处理数据库会话的创建和关闭，包括在请求处理过程中发生异常时的清理工作。
3. 测试便利性 ：依赖注入使得在测试时可以轻松替换真实的数据库会话为模拟对象。
4. 请求作用域 ：依赖注入确保每个请求获得独立的数据库会话，避免会话共享导致的并发问题。
5. 中间件集成 ：依赖系统与FastAPI的中间件和异常处理系统无缝集成，确保资源在所有情况下都能正确释放。
'''


# 获取用户列表（分页）
@app.get("/users", response_model=dict)
async def get_users(
        pagination: PaginationParams = Depends(),
        username: Optional[str] = Query(None, description="按用户名过滤"),
        email: Optional[str] = Query(None, description="按邮箱过滤"),
        db: AsyncSession = Depends(get_db)
):
    # 构建查询
    query = select(User)
    count_query = select(func.count()).select_from(User)

    # 应用过滤条件
    if username:
        query = query.filter(User.username.contains(username))
        count_query = count_query.filter(User.username.contains(username))
    if email:
        query = query.filter(User.email.contains(email))
        count_query = count_query.filter(User.email.contains(email))

    # 执行计数查询
    total_result = await db.execute(count_query)
    total = total_result.scalar()

    # 应用分页
    offset = (pagination.page - 1) * pagination.size
    # 添加joinedload预加载用户的订单数据
    query = query.options(joinedload(User.orders)).offset(offset).limit(pagination.size)

    # 执行分页查询
    result = await db.execute(query)
    # 添加unique()方法调用以处理一对多关系导致的重复记录
    users = result.unique().scalars().all()

    # 构建分页响应
    response = PaginationResponse(
        items=[{
            "id": user.id,
            "username": user.username,
            "email": user.email,
            "full_name": user.full_name,
            "created_at": user.created_at,
            "updated_at": user.updated_at,
            "orders": [{
                "id": order.id,
                "product": order.product,
                "amount": order.amount,
                "created_at": order.created_at,
                "updated_at": order.updated_at
            } for order in user.orders]
        } for user in users],
        total=total,
        page=pagination.page,
        size=pagination.size
    )

    return response.dict()


# 获取单个用户
@app.get("/users/{user_id}", response_model=UserResponse,
         response_model_exclude={"email", "orders.user_id"})
async def get_user(user_id: int, db: AsyncSession = Depends(get_db)):
    result = await db.execute(select(User).filter(User.id == user_id).options(joinedload(User.orders)))
    user = result.scalars().first()
    '''
    在`get_user` 函数中不需要调用`unique()` 方法是因为查询条件`User.id == user_id` 
    已经确保了结果是唯一的。用户ID是主键，所以通过ID查询只会返回一条记录。
    而在`get_users` 函数中需要使用`unique()` 是因为那里使用了`joinedload(User.orders)` 来加载一对多关系（一个用户有多个订单），
    这会导致结果集中出现重复的用户记录（每个订单对应一条用户记录）。
    `unique()` 方法确保在处理结果时每个用户只被返回一次，同时保留所有关联的订单数据。
    '''
    if not user:
        raise HTTPException(status_code=404, detail=f"用户ID {user_id} 不存在")

    return user


# 创建新用户
@app.post("/users", response_model=UserResponse, status_code=201)
async def create_user(user: UserCreate, db: AsyncSession = Depends(get_db)):
    # 检查用户名是否已存在
    result = await db.execute(select(User).filter(User.username == user.username))
    if result.scalars().first():
        raise HTTPException(status_code=400, detail=f"用户名 {user.username} 已存在")

    # 检查邮箱是否已存在
    result = await db.execute(select(User).filter(User.email == user.email))
    if result.scalars().first():
        raise HTTPException(status_code=400, detail=f"邮箱 {user.email} 已存在")

    # 创建新用户
    new_user = User(
        username=user.username,
        email=user.email,
        full_name=user.full_name
    )

    # 添加到数据库并提交
    db.add(new_user)
    await db.commit()
    await db.refresh(new_user)

    return new_user


# 修改用户信息
@app.put("/users/{user_id}", response_model=UpdateUserResponse,
         response_model_exclude_unset=True)
async def update_user(user_id: int, user: UserUpdate, db: AsyncSession = Depends(get_db)):
    # 查询用户是否存在
    result = await db.execute(select(User).filter(User.id == user_id))
    existing_user = result.scalars().first()

    if not existing_user:
        raise HTTPException(status_code=404, detail=f"用户ID {user_id} 不存在")

    # 检查用户名是否已被其他用户使用
    if user.username is not None and user.username != existing_user.username:
        result = await db.execute(select(User).filter(User.username == user.username))
        if result.scalars().first():
            raise HTTPException(status_code=400, detail=f"用户名 {user.username} 已存在")

    # 检查邮箱是否已被其他用户使用
    if user.email is not None and user.email != existing_user.email:
        result = await db.execute(select(User).filter(User.email == user.email))
        if result.scalars().first():
            raise HTTPException(status_code=400, detail=f"邮箱 {user.email} 已存在")

    # 更新用户信息
    if user.username is not None:
        existing_user.username = user.username
    if user.email is not None:
        existing_user.email = user.email
    if user.full_name is not None:
        existing_user.full_name = user.full_name

    # 提交更改
    await db.commit()
    await db.refresh(existing_user)

    return existing_user


# 删除用户
@app.delete("/users/{user_id}", status_code=200)
async def delete_user(user_id: int, db: AsyncSession = Depends(get_db)):
    # 查询用户是否存在
    result = await db.execute(select(User).filter(User.id == user_id).options(joinedload(User.orders)))
    user = result.scalars().first()

    if not user:
        raise HTTPException(status_code=404, detail=f"用户ID {user_id} 不存在")

    # 先删除用户的所有订单
    if user.orders:
        for order in user.orders:
            await db.delete(order)

    # 然后删除用户
    await db.delete(user)
    await db.commit()

    return {"message": f"用户ID {user_id} 及其所有订单已成功删除"}


# 通过订单ID查询订单
@app.get("/orders/{order_id}", response_model=dict)
async def get_order_by_id(order_id: int, db: AsyncSession = Depends(get_db)):
    # 构建查询，使用joinedload加载关联的用户信息
    query = select(Order).filter(Order.id == order_id).options(joinedload(Order.user))
    
    # 执行查询
    result = await db.execute(query)
    order = result.scalars().first()
    
    # 检查订单是否存在
    if not order:
        raise HTTPException(status_code=404, detail=f"订单ID {order_id} 不存在")
    
    # 构建响应数据
    response = {
        "id": order.id,
        "product": order.product,
        "amount": order.amount,
        "created_at": order.created_at.strftime('%Y-%m-%d %H:%M:%S'),
        "updated_at": order.updated_at.strftime('%Y-%m-%d %H:%M:%S'),
        "user": {
            "id": order.user.id,
            "username": order.user.username,
            "email": order.user.email,
            "full_name": order.user.full_name,
            "created_at": order.user.created_at.strftime('%Y-%m-%d %H:%M:%S'),
            "updated_at": order.user.updated_at.strftime('%Y-%m-%d %H:%M:%S')
        }
    }
    
    return response


# 运行应用（开发环境）
if __name__ == "__main__":
    import uvicorn

    uvicorn.run("main:app", host="0.0.0.0", port=8001, reload=True)
